package perform.lazy.impl;

import java.sql.Connection;
import java.util.Arrays;
import java.util.List;

import perform.annotations.ManyToMany;
import perform.annotations.OneToMany;
import perform.criterion.EqualNullSafe;
import perform.event.LoadEvent;
import perform.exception.LoadException;
import perform.lazy.PerformLoader;
import perform.mappings.ClassMappings;
import perform.mappings.ManyToManyMapping;
import perform.query.impl.M2MLoadQuery;
import perform.query.impl.RetrieveQuery;
import perform.query.impl.SelectQuery;
import perform.type.Type;
import perform.utilities.ResultProcessor;

public class SimpleLoader implements PerformLoader {
	
	private ResultProcessor processor;
	
	public SimpleLoader(){
		processor = new ResultProcessor();
	}

	@Override
	public void loadManyToOne(LoadEvent event, Connection connection)
			throws LoadException {
		try {
			Type m2o = (Type)event.getSourceField().get(event.getSource());
			RetrieveQuery query = new RetrieveQuery(m2o, connection);
			m2o = processor.process(m2o, query.executeQuery());
			query.close();
			event.getSourceField().set(event.getSource(), m2o);
		} catch (Exception e) {
			throw new LoadException(e);
		}
	}

	@Override
	public void loadOneToOne(LoadEvent event, Connection connection)
			throws LoadException {
		try {
			Type o2o = (Type)event.getSourceField().get(event.getSource());
			RetrieveQuery query = new RetrieveQuery(o2o, connection);
			o2o = processor.process(o2o, query.executeQuery());
			query.close();
			event.getSourceField().set(event.getSource(), o2o);
		} catch (Exception e) {
			throw new LoadException(e);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T extends Type> void loadOneToMany(LoadEvent event,
			Connection connection) throws LoadException {
		try {
			OneToMany o2m = event.getSourceField().getAnnotation(OneToMany.class);
			SelectQuery query = new SelectQuery(o2m.target(), EqualNullSafe.create(o2m.joinColumn(), 
					ClassMappings.getIDField(event.getSource().getClass()).get(event.getSource())), connection);
			T[] o2ms = processor.processArray((Class<T>)o2m.target(), query.executeQuery());
			query.close();
			if(List.class.isAssignableFrom(event.getSourceField().getType()) &&  o2ms != null){
				event.getSourceField().set(event.getSource(), Arrays.asList(o2ms));
			} else if(o2ms != null){
				event.getSourceField().set(event.getSource(), o2ms);
			}
		} catch (Exception e) {
			throw new LoadException(e);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T extends Type> void loadManyToMany(LoadEvent event,
			Connection connection) throws LoadException {
		try {
			ManyToMany m2m = event.getSourceField().getAnnotation(ManyToMany.class); 
			ManyToManyMapping mapping = ClassMappings.getManyToManyMapping(
					m2m.table());
			M2MLoadQuery query = new M2MLoadQuery(mapping, event.getSource(), event.getSourceField(), connection);
			T[] m2ms = processor.processArray((Class<T>)m2m.target(), query.executeQuery());
			query.close();
			if(List.class.isAssignableFrom(event.getSourceField().getType()) && m2ms != null){
				event.getSourceField().set(event.getSource(), Arrays.asList(m2ms));
			} else if(m2ms != null){
				event.getSourceField().set(event.getSource(), m2ms);
			}
		} catch (Exception e) {
			throw new LoadException(e);
		}
	}

}
